This is an R Markdown
Notebook. When you execute code within the notebook, the results appear
beneath the code.
Try executing this chunk by clicking the Run button within
the chunk or by placing your cursor inside it and pressing
Cmd+Shift+Enter.
Install packages
# install.packages("readr")
# install.packages("dplyr")
# install.packages("stringr")
# install.packages("shiny")
# install.packages("ggplot2")
# install.packages("plotly")
Load in packages
# Allows us to read-in csv files
library(readr)
# For data manipulation
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
# For regular expression operations
library(stringr)
# library(shiny)
library(ggplot2)
# Used tp create interactive visualisations
library(plotly)
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
Load-in dataset
df <- read_csv('Data/GI_age.csv')
Rows: 42 Columns: 7── Column specification ────────────────────────────────────
Delimiter: ","
chr (4): England and Wales Code, England and Wales, Gend...
dbl (3): Gender identity (7 categories) Code, Age (6 cat...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
# But can also click on the dataset in the Environment pane
head(df, 10)
# Let's check out the dimensions
dim(df)
[1] 42 7
Data Cleaning
# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df) <- str_replace_all(colnames(df), "\\s*\\([^)]*\\)", "")
# Lowercase column text and replace empty spaces with "_"
colnames(df) <- tolower(colnames(df))
colnames(df) <- str_replace_all(colnames(df), " ", "_")
# Let's see if it worked..
head(df)
Pipes and other operators..
So, we’ve already come across the assignment operator ‘<-’ which
is used to assign a value. E.g. df <- read_csv(‘Data/GI_age.csv’),
here we assign our csv file to a dataframe variable called ‘df’.
But, we’re now going to encounter the pipe operator ‘%>%’ which
can seem intimidating at first but is actually pretty simple. It’s used
to pass the result of one function directly into the next function. E.g.
df <- df %>% filter(gender_identity_code != -8), here we start
with our df and pass it to the filter function using the pipe operator.
This basically supplies the filter() function with its first argument,
which is the dataframe to filter on. And here we encounter a logical
operator ‘!=’ within the filter() function, which specifies that we
should only keep rows where gender_identity_code is not equal to -8.
# Get rid of columns with 0 observations
df <- df %>%
filter(gender_identity_code != -8)
# Check it worked
head(df, 10)
# Get rid of redundant age category
# Further filter data
df <- df %>%
filter(age_code != 1)
# Clean up the values in the 'age' column. Let's shorten them.
# Chain str_replace() calls together to apply multiple string replacements in succession
# Each str_replace() call is applied to the result of the previous one
df$age <- df$age %>%
str_replace('Aged ', '') %>%
str_replace('to', '-') %>%
str_replace('years', '') %>%
str_replace('and over', '+')
# We can pass our df to the select function, where we specify the column we're interested in.
# Then, we pipe the output to the head function.
df %>%
select(age) %>%
head()
Question
How is gender identity distributed among different age groups?
Some subquestions that this can help us answer:
- What % of trans men are aged 16-24 years?
- Are older age groups overrepresented in the ‘non-response’
category?
Data pre-processing
Calculate percentages
Below, we use the group_by function to group the data by
‘gender_identity’ and calculate the percentage within each group. Then
the mutate() function adds a new column ‘percentage’ to df, which (for
each group) divides the observation by the sum of observations,
multiplies it by 100, and rounds it up to 2 decimal points. We then use
the ungroup function when we’re done with the grouping operation.
df <- df %>%
group_by(gender_identity) %>%
mutate(percentage = round((observation / sum(observation) * 100), 2)) %>%
ungroup()
head(df)
# Directly convert to a factor with the specified order
df$gender_identity <- factor(df$gender_identity, levels = c(
"Gender identity the same as sex registered at birth",
"Gender identity different from sex registered at birth but no specific identity given",
"Trans woman",
"Trans man",
"All other gender identities",
"Not answered"
))
# Print the levels to ensure they are correct
print(levels(df$gender_identity))
[1] "Gender identity the same as sex registered at birth"
[2] "Gender identity different from sex registered at birth but no specific identity given"
[3] "Trans woman"
[4] "Trans man"
[5] "All other gender identities"
[6] "Not answered"
Interactive grouped bar chart + stacked bar chart
So, the convention when using Plotly in R, is to create our plot
first by using the ggplot2 package. Then, we convert the ggplot object
to a ‘plotly’ object using ‘ggplotly’. There’s a lot going on here so
I’ll break some of it down. The ggplot() function initialises a ggplot
object, which sets up the dataframe that will be used for the plot and
specifies the aesthetic mappings which describe how variables in the
data are mapped to visual properties. So, inside aes() we specify our x
and y columns, and specify that we want to map our age column to fill
the colour of the bars.
Meanwhile, geom_bar() is used to make bar charts, so it adds the bar
geometry to the plot. And we set stat to ‘identity’, which tells
‘ggplot’ to use the value in the y-axis column (‘percentage’) for the
height of the bars. By setting position to ‘dodge’ we ensure that the
bars are placed next to each other.
Finally, labs() is used to add or modify labels, and theme is used to
customise non-data parts of the plot like text, legend, axes. And
scale_fill_discrete() controls the colour scales and here we use the
name parameter to label our legend “Age”.
TLDR: we’re using the + operator and ggplot functions to build upon
the base ggplot object, layering on aesthetic mappings, geometries,
labels, etc.
p <- ggplot(df, aes(x = gender_identity, y = percentage, fill = age,
text = paste('Observation:', observation))) + # Include observation info
geom_bar(stat = "identity", position = "dodge") +
labs(title = 'Distribution of Gender Identity Categories Among Age Groups',
x = 'Gender Identity', y = 'Percentage') +
theme(plot.title = element_text(hjust = 0.5)) +
scale_fill_discrete(name = "Age")
# Let's take a look at our static graph
p

Hmm okay. Not too shabby, but we’re definitely going to have to do
something about our x-axis labels, as right now everything is pretty
cluttered. Maybe we could rotate them, or just rename them. We’ll get
round to it. But for now, let’s make this thing interactive.
# Convert ggplot object to a plotly object for interactivity
fig <- ggplotly(p, tooltip = c("y", "fill", "text"), width = 700, height = 500) # Specify tooltip components
# Let's check it out
fig
NA
tickvals <- 1:length(levels(df$gender_identity))
# Custom tick labels corresponding to the levels
ticktext <- c(
"Cisgender",
"Gender identity different from sex",
"Trans woman",
"Trans man",
"All other identities",
"Not answered"
)
Dataset 2
# Load in dataset
df2 <- read_csv('Data/GI_ethnic.csv')
Rows: 10592 Columns: 7── Column specification ────────────────────────────────────
Delimiter: ","
chr (4): Lower tier local authorities Code, Lower tier l...
dbl (3): Gender identity (4 categories) Code, Ethnic gro...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse at underlying data structure
head(df2, 10)
# Remove all text within parentheses from column names and replace it with an empty string
# tilde operator (~) used to apply function 'gsub' to each colname
# .x represents each colname that gsub will be applied to
df2 <- df2 %>%
rename_with(~ gsub("\\s*\\([^)]*\\)", "", .x))
# Lowercase all text in column names and replace spaces with underscores
df2 <- df2 %>%
rename_with(~ tolower(gsub(" ", "_", .x)))
# Shorten the local authority column names as they are way too long
df2 <- df2 %>%
rename(LA_code = lower_tier_local_authorities_code,
LA_name = lower_tier_local_authorities)
# Let's see if it worked
colnames(df2)
[1] "LA_code" "LA_name"
[3] "gender_identity_code" "gender_identity"
[5] "ethnic_group_code" "ethnic_group"
[7] "observation"
# Remove 'Does not apply' categories for the gender identity and ethnic group columns
df2 <- df2 %>%
filter(gender_identity_code != -8, ethnic_group_code != -8)
# Display the first 30 rows
head(df2, 30)
Data pre-processing
Calculate % of each ethnic group in each LA
# First, we're going to group our data by LA_name, Ethnic group, and sum our observations
# This leaves us with the total of each ethnic group in each local authority
ethnic_totals <- df2 %>%
group_by(LA_name, ethnic_group) %>%
summarise(Ethnic_sum = sum(observation, na.rm = TRUE)) %>%
ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
# Print the first few rows to check
head(ethnic_totals)
# Calculate total observations for each local authority
la_totals <- df2 %>%
group_by(LA_name) %>%
summarise(LA_sum = sum(observation, na.rm = TRUE)) %>%
ungroup()
# Print the first few rows to check
head(la_totals)
# Merge the summed ethnic group data with the total LA observations to calculate percentage of each ethnic group in each LA
merged <- merge(ethnic_totals, la_totals, by = "LA_name")
# Calculate the percentage of each ethnic group within each local authority
merged <- merged %>%
mutate(Percentage = round((Ethnic_sum / LA_sum * 100), 2))
# Print the first few rows to check
head(merged, 10)
Calculate Non-Response Rates Within LAs
# Now calculate the non-response % for each ethnic group in each LA
# This involves grouping by LA_name, ethnic_group, and summing observations again
ethnic_group_totals <- df2 %>%
group_by(LA_name, ethnic_group) %>%
summarise(Ethnic_group_total = sum(observation, na.rm = TRUE)) %>%
ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
# This is the sum of non-responses for each ethnic group within each LA
non_response_totals <- df2 %>%
filter(gender_identity == 'Not answered') %>%
group_by(LA_name, ethnic_group) %>%
summarise(Non_response_total = sum(observation, na.rm = TRUE)) %>%
ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
head(non_response_totals)
# Merge the totals with the non-response totals
merged_data <- merge(ethnic_group_totals, non_response_totals, by = c("LA_name", "ethnic_group"), all.x = TRUE)
head(merged_data)
# Calculate the non-response percentage for each ethnic group within each LA
merged_data <- merged_data %>%
mutate(Eth_NR_Perc = round((Non_response_total / Ethnic_group_total * 100), 2))
head(merged_data)
# Merge the non-response data with the percentage of each ethnic group within each LA
nr <- merge(merged_data, select(merged, LA_name, ethnic_group, Percentage), by = c("LA_name", "ethnic_group"))
head(nr)
Interactive scatterplot
In this section we’re going to:
Create a simple scatterplot exploring the relationship between
the percentage of asian citizens within local authorities and their
non-response rates
Implement a widget to update our scatterplot
# Subset dataframe so we only have responses from the asian ethnic group
asian <- nr %>%
filter(ethnic_group == 'Asian, Asian British or Asian Welsh')
head(asian)
# Initialize figure
fig <- plot_ly(data = asian,
x = ~Percentage,
y = ~Eth_NR_Perc,
text = ~paste('LA Name:', LA_name,
'<br>Percentage:', sprintf("%.2f", Percentage),
'<br>Non-response Rate:', sprintf("%.2f%%", Eth_NR_Perc),
'<br>Non-response Total:', Non_response_total,
'<br>Ethnic Group Total:', Ethnic_group_total),
hoverinfo = "text",
mode = 'markers', # Specify marker points
type = 'scatter', # Graph type - scatterplot
name = 'Asian') # Default visible graph
# Customize layout
fig <- fig %>%
layout(title = 'Non-Response Rates of the Asian Ethnic Group Across Local Authorities',
xaxis = list(title = 'Percentage of Ethnic Group'),
yaxis = list(title = 'Non-response Rate'),
width = 900,
height = 900)
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
# Show the plot
fig
Dropdown selection
What we’re going to do now, is use Plotly’s ‘updatemenus’ in
conjunction with the ‘update’ method to create a dropdown where we can
switch between the Asian ethnic group, and White.
Step 1: Initialise figure and add traces
We’ll start by creating a plot_ly figure. We use plotly figures here
instead of ggplotly, because plot_ly objects offer more control over how
plots are constructed. It allows us to add ‘traces’, which refer to a
set of data. In our example, we want to add a trace with the data points
relating to our asian ethnic group, and another one for our white ethnic
group. This will start to make sense when we look at the code below.
# Initialize a Plotly figure
fig <- plot_ly()
fig
Warning: No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Warning: No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
# Add trace for the Asian ethnic group
fig <- fig %>% add_trace(
data = nr[nr$ethnic_group == 'Asian, Asian British or Asian Welsh',],
x = ~Percentage,
y = ~Eth_NR_Perc,
text = ~paste('LA Name:', LA_name),
type = 'scatter',
mode = 'markers',
name = 'Asian',
hoverinfo = 'text+x+y',
visible = T
)
# Add trace for the White ethnic group
fig <- fig %>% add_trace(
data = nr[nr$ethnic_group == 'White: English, Welsh, Scottish, Northern Irish or British',],
x = ~Percentage,
y = ~Eth_NR_Perc,
text = ~paste('LA Name:', LA_name),
type = 'scatter',
mode = 'markers',
name = 'White',
hoverinfo = 'text+x+y',
visible = F
)
fig
# Define dropdown buttons for interactivity
fig <- fig %>% layout(
title = "Non-Response Rates Across Local Authorities",
xaxis = list(title = "Percentage of Ethnic Group"),
yaxis = list(title = "Non-response Rate"),
showlegend = FALSE,
updatemenus = list(
list(
type = "dropdown",
buttons = list(
list(
method = "update",
args = list(list("visible" = list(TRUE, FALSE)),
list("title" = "Non-Response Rates of the Asian Ethnic Group Across Local Authorities")),
label = "Asian"
),
list(
method = "update",
args = list(list("visible" = list(FALSE, TRUE)),
list("title" = "Non-Response Rates of the White Ethnic Group Across Local Authorities")),
label = "White"
)
)
)
)
)
# Display the figure
fig
Add a new chunk by clicking the Insert Chunk button on the
toolbar or by pressing Cmd+Option+I.
When you save the notebook, an HTML file containing the code and
output will be saved alongside it (click the Preview button or
press Cmd+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the
editor. Consequently, unlike Knit, Preview does not
run any R code chunks. Instead, the output of the chunk when it was last
run in the editor is displayed.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgojIyBJbnN0YWxsIHBhY2thZ2VzCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQojIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQojIGluc3RhbGwucGFja2FnZXMoInNoaW55IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMjIExvYWQgaW4gcGFja2FnZXMKCmBgYHtyfQojIEFsbG93cyB1cyB0byByZWFkLWluIGNzdiBmaWxlcwpsaWJyYXJ5KHJlYWRyKSAKIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgCiMgRm9yIHJlZ3VsYXIgZXhwcmVzc2lvbiBvcGVyYXRpb25zIApsaWJyYXJ5KHN0cmluZ3IpIAojIGxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKIyBVc2VkIHRwIGNyZWF0ZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucwpsaWJyYXJ5KHBsb3RseSkKYGBgCiMjIExvYWQtaW4gZGF0YXNldAoKYGBge3J9CmRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBvZiBkYXRhIHN0cnVjdHVyZQojIEJ1dCBjYW4gYWxzbyBjbGljayBvbiB0aGUgZGF0YXNldCBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZQpgYGAKCgpgYGB7cn0KaGVhZChkZiwgMTApCmBgYAoKYGBge3J9CiMgTGV0J3MgY2hlY2sgb3V0IHRoZSBkaW1lbnNpb25zCgpkaW0oZGYpCmBgYAoKIyMgRGF0YSBDbGVhbmluZwoKYGBge3J9CiMgc3RyX3JlcGxhY2VfYWxsKCkgbWV0aG9kIGZpbmRzIGFsbCBzdWJzdHJpbmdzIHdoaWNoIG1hdGNoIHRoZSByZWdleCBhbmQgcmVwbGFjZXMgdGhlbSB3aXRoIGVtcHR5IHN0cmluZwojIEZpcnN0LCBsZXQncyByZXBsYWNlIGFueSBicmFja2V0cyB3aXRoIGVtcHR5IHN0cmluZ3MKY29sbmFtZXMoZGYpIDwtIHN0cl9yZXBsYWNlX2FsbChjb2xuYW1lcyhkZiksICJcXHMqXFwoW14pXSpcXCkiLCAiIikKCiMgTG93ZXJjYXNlIGNvbHVtbiB0ZXh0IGFuZCByZXBsYWNlIGVtcHR5IHNwYWNlcyB3aXRoICJfIgpjb2xuYW1lcyhkZikgPC0gdG9sb3dlcihjb2xuYW1lcyhkZikpCmNvbG5hbWVzKGRmKSA8LSBzdHJfcmVwbGFjZV9hbGwoY29sbmFtZXMoZGYpLCAiICIsICJfIikKCiMgTGV0J3Mgc2VlIGlmIGl0IHdvcmtlZC4uCmhlYWQoZGYpCmBgYAoKIyMjIFBpcGVzIGFuZCBvdGhlciBvcGVyYXRvcnMuLgoKU28sIHdlJ3ZlIGFscmVhZHkgY29tZSBhY3Jvc3MgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgJzwtJyB3aGljaCBpcyB1c2VkIHRvIGFzc2lnbiBhIHZhbHVlLiBFLmcuIGRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKSwgaGVyZSB3ZSBhc3NpZ24gb3VyIGNzdiBmaWxlIHRvIGEgZGF0YWZyYW1lIHZhcmlhYmxlIGNhbGxlZCAnZGYnLgoKQnV0LCB3ZSdyZSBub3cgZ29pbmcgdG8gZW5jb3VudGVyIHRoZSBwaXBlIG9wZXJhdG9yICclPiUnIHdoaWNoIGNhbiBzZWVtIGludGltaWRhdGluZyBhdCBmaXJzdCBidXQgaXMgYWN0dWFsbHkgcHJldHR5IHNpbXBsZS4gSXQncyB1c2VkIHRvIHBhc3MgdGhlIHJlc3VsdCBvZiBvbmUgZnVuY3Rpb24gZGlyZWN0bHkgaW50byB0aGUgbmV4dCBmdW5jdGlvbi4gRS5nLiBkZiA8LSBkZiAlPiUgZmlsdGVyKGdlbmRlcl9pZGVudGl0eV9jb2RlICE9IC04KSwgaGVyZSB3ZSBzdGFydCB3aXRoIG91ciBkZiBhbmQgcGFzcyBpdCB0byB0aGUgZmlsdGVyIGZ1bmN0aW9uIHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yLiBUaGlzIGJhc2ljYWxseSBzdXBwbGllcyB0aGUgZmlsdGVyKCkgZnVuY3Rpb24gd2l0aCBpdHMgZmlyc3QgYXJndW1lbnQsIHdoaWNoIGlzIHRoZSBkYXRhZnJhbWUgdG8gZmlsdGVyIG9uLiBBbmQgaGVyZSB3ZSBlbmNvdW50ZXIgYSBsb2dpY2FsIG9wZXJhdG9yICchPScgd2l0aGluIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiwgd2hpY2ggc3BlY2lmaWVzIHRoYXQgd2Ugc2hvdWxkIG9ubHkga2VlcCByb3dzIHdoZXJlIGdlbmRlcl9pZGVudGl0eV9jb2RlIGlzIG5vdCBlcXVhbCB0byAtOC4gCgpgYGB7cn0KIyBHZXQgcmlkIG9mIGNvbHVtbnMgd2l0aCAwIG9ic2VydmF0aW9ucwpkZiA8LSBkZiAlPiUgCiAgZmlsdGVyKGdlbmRlcl9pZGVudGl0eV9jb2RlICE9IC04KSAKCiMgQ2hlY2sgaXQgd29ya2VkCgpoZWFkKGRmLCAxMCkKYGBgCgpgYGB7cn0KIyBHZXQgcmlkIG9mIHJlZHVuZGFudCBhZ2UgY2F0ZWdvcnkKIyBGdXJ0aGVyIGZpbHRlciBkYXRhCmRmIDwtIGRmICU+JQogIGZpbHRlcihhZ2VfY29kZSAhPSAxKQoKYGBgCgpgYGB7cn0KIyBDbGVhbiB1cCB0aGUgdmFsdWVzIGluIHRoZSAnYWdlJyBjb2x1bW4uIExldCdzIHNob3J0ZW4gdGhlbS4KCiMgQ2hhaW4gc3RyX3JlcGxhY2UoKSBjYWxscyB0b2dldGhlciB0byBhcHBseSBtdWx0aXBsZSBzdHJpbmcgcmVwbGFjZW1lbnRzIGluIHN1Y2Nlc3Npb24KIyBFYWNoIHN0cl9yZXBsYWNlKCkgY2FsbCBpcyBhcHBsaWVkIHRvIHRoZSByZXN1bHQgb2YgdGhlIHByZXZpb3VzIG9uZQpkZiRhZ2UgPC0gZGYkYWdlICU+JQogIHN0cl9yZXBsYWNlKCdBZ2VkICcsICcnKSAlPiUKICBzdHJfcmVwbGFjZSgndG8nLCAnLScpICU+JQogIHN0cl9yZXBsYWNlKCd5ZWFycycsICcnKSAlPiUKICBzdHJfcmVwbGFjZSgnYW5kIG92ZXInLCAnKycpCgojIFdlIGNhbiBwYXNzIG91ciBkZiB0byB0aGUgc2VsZWN0IGZ1bmN0aW9uLCB3aGVyZSB3ZSBzcGVjaWZ5IHRoZSBjb2x1bW4gd2UncmUgaW50ZXJlc3RlZCBpbi4KIyBUaGVuLCB3ZSBwaXBlIHRoZSBvdXRwdXQgdG8gdGhlIGhlYWQgZnVuY3Rpb24uCmRmICU+JQogIHNlbGVjdChhZ2UpICU+JQogIGhlYWQoKQpgYGAKCiMjIFF1ZXN0aW9uCgpIb3cgaXMgZ2VuZGVyIGlkZW50aXR5IGRpc3RyaWJ1dGVkIGFtb25nIGRpZmZlcmVudCBhZ2UgZ3JvdXBzPwoKU29tZSBzdWJxdWVzdGlvbnMgdGhhdCB0aGlzIGNhbiBoZWxwIHVzIGFuc3dlcjoKCiogV2hhdCAlIG9mIHRyYW5zIG1lbiBhcmUgYWdlZCAxNi0yNCB5ZWFycz8KKiBBcmUgb2xkZXIgYWdlIGdyb3VwcyBvdmVycmVwcmVzZW50ZWQgaW4gdGhlICdub24tcmVzcG9uc2UnIGNhdGVnb3J5PwoKIyMgRGF0YSBwcmUtcHJvY2Vzc2luZwoKIyMjIENhbGN1bGF0ZSBwZXJjZW50YWdlcyAKCkJlbG93LCB3ZSB1c2UgdGhlIGdyb3VwX2J5IGZ1bmN0aW9uIHRvIGdyb3VwIHRoZSBkYXRhIGJ5ICdnZW5kZXJfaWRlbnRpdHknIGFuZCBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugd2l0aGluIGVhY2ggZ3JvdXAuIFRoZW4gdGhlIG11dGF0ZSgpIGZ1bmN0aW9uIGFkZHMgYSBuZXcgY29sdW1uICdwZXJjZW50YWdlJyB0byBkZiwgd2hpY2ggKGZvciBlYWNoIGdyb3VwKSBkaXZpZGVzIHRoZSBvYnNlcnZhdGlvbiBieSB0aGUgc3VtIG9mIG9ic2VydmF0aW9ucywgbXVsdGlwbGllcyBpdCBieSAxMDAsIGFuZCByb3VuZHMgaXQgdXAgdG8gMiBkZWNpbWFsIHBvaW50cy4gV2UgdGhlbiB1c2UgdGhlIHVuZ3JvdXAgZnVuY3Rpb24gd2hlbiB3ZSdyZSBkb25lIHdpdGggdGhlIGdyb3VwaW5nIG9wZXJhdGlvbi4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoZ2VuZGVyX2lkZW50aXR5KSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IHJvdW5kKChvYnNlcnZhdGlvbiAvIHN1bShvYnNlcnZhdGlvbikgKiAxMDApLCAyKSkgJT4lCiAgdW5ncm91cCgpCgpoZWFkKGRmKQpgYGAKCmBgYHtyfQojIERpcmVjdGx5IGNvbnZlcnQgdG8gYSBmYWN0b3Igd2l0aCB0aGUgc3BlY2lmaWVkIG9yZGVyCmRmJGdlbmRlcl9pZGVudGl0eSA8LSBmYWN0b3IoZGYkZ2VuZGVyX2lkZW50aXR5LCBsZXZlbHMgPSBjKAogICJHZW5kZXIgaWRlbnRpdHkgdGhlIHNhbWUgYXMgc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGgiLAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGggYnV0IG5vIHNwZWNpZmljIGlkZW50aXR5IGdpdmVuIiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgZ2VuZGVyIGlkZW50aXRpZXMiLAogICJOb3QgYW5zd2VyZWQiCikpCmBgYAoKYGBge3J9CiMgUHJpbnQgdGhlIGxldmVscyB0byBlbnN1cmUgdGhleSBhcmUgY29ycmVjdApwcmludChsZXZlbHMoZGYkZ2VuZGVyX2lkZW50aXR5KSkKCmBgYAoKCiMjIEludGVyYWN0aXZlIGdyb3VwZWQgYmFyIGNoYXJ0ICsgc3RhY2tlZCBiYXIgY2hhcnQKClNvLCB0aGUgY29udmVudGlvbiB3aGVuIHVzaW5nIFBsb3RseSBpbiBSLCBpcyB0byBjcmVhdGUgb3VyIHBsb3QgZmlyc3QgYnkgdXNpbmcgdGhlIGdncGxvdDIgcGFja2FnZS4gVGhlbiwgd2UgY29udmVydCB0aGUgZ2dwbG90IG9iamVjdCB0byBhICdwbG90bHknIG9iamVjdCB1c2luZyAnZ2dwbG90bHknLiBUaGVyZSdzIGEgbG90IGdvaW5nIG9uIGhlcmUgc28gSSdsbCBicmVhayBzb21lIG9mIGl0IGRvd24uIFRoZSBnZ3Bsb3QoKSBmdW5jdGlvbiBpbml0aWFsaXNlcyBhIGdncGxvdCBvYmplY3QsIHdoaWNoIHNldHMgdXAgdGhlIGRhdGFmcmFtZSB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgdGhlIHBsb3QgYW5kIHNwZWNpZmllcyB0aGUgYWVzdGhldGljIG1hcHBpbmdzIHdoaWNoIGRlc2NyaWJlIGhvdyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgYXJlIG1hcHBlZCB0byB2aXN1YWwgcHJvcGVydGllcy4gU28sIGluc2lkZSBhZXMoKSB3ZSBzcGVjaWZ5IG91ciB4IGFuZCB5IGNvbHVtbnMsIGFuZCBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0byBtYXAgb3VyIGFnZSBjb2x1bW4gdG8gZmlsbCB0aGUgY29sb3VyIG9mIHRoZSBiYXJzLgoKTWVhbndoaWxlLCBnZW9tX2JhcigpIGlzIHVzZWQgdG8gbWFrZSBiYXIgY2hhcnRzLCBzbyBpdCBhZGRzIHRoZSBiYXIgZ2VvbWV0cnkgdG8gdGhlIHBsb3QuIEFuZCB3ZSBzZXQgc3RhdCB0byAnaWRlbnRpdHknLCB3aGljaCB0ZWxscyAnZ2dwbG90JyB0byB1c2UgdGhlIHZhbHVlIGluIHRoZSB5LWF4aXMgY29sdW1uICgncGVyY2VudGFnZScpIGZvciB0aGUgaGVpZ2h0IG9mIHRoZSBiYXJzLiBCeSBzZXR0aW5nIHBvc2l0aW9uIHRvICdkb2RnZScgd2UgZW5zdXJlIHRoYXQgdGhlIGJhcnMgYXJlIHBsYWNlZCBuZXh0IHRvIGVhY2ggb3RoZXIuIAoKRmluYWxseSwgbGFicygpIGlzIHVzZWQgdG8gYWRkIG9yIG1vZGlmeSBsYWJlbHMsIGFuZCB0aGVtZSBpcyB1c2VkIHRvIGN1c3RvbWlzZSBub24tZGF0YSBwYXJ0cyBvZiB0aGUgcGxvdCBsaWtlIHRleHQsIGxlZ2VuZCwgYXhlcy4gQW5kIHNjYWxlX2ZpbGxfZGlzY3JldGUoKSBjb250cm9scyB0aGUgY29sb3VyIHNjYWxlcyBhbmQgaGVyZSB3ZSB1c2UgdGhlIG5hbWUgcGFyYW1ldGVyIHRvIGxhYmVsIG91ciBsZWdlbmQgIkFnZSIuIAoKVExEUjogd2UncmUgdXNpbmcgdGhlICsgb3BlcmF0b3IgYW5kIGdncGxvdCBmdW5jdGlvbnMgdG8gYnVpbGQgdXBvbiB0aGUgYmFzZSBnZ3Bsb3Qgb2JqZWN0LCBsYXllcmluZyBvbiBhZXN0aGV0aWMgbWFwcGluZ3MsIGdlb21ldHJpZXMsIGxhYmVscywgZXRjLgoKYGBge3J9CnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGdlbmRlcl9pZGVudGl0eSwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSBhZ2UsCiAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCdPYnNlcnZhdGlvbjonLCBvYnNlcnZhdGlvbikpKSArICAjIEluY2x1ZGUgb2JzZXJ2YXRpb24gaW5mbwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBsYWJzKHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBHZW5kZXIgSWRlbnRpdHkgQ2F0ZWdvcmllcyBBbW9uZyBBZ2UgR3JvdXBzJywKICAgICAgIHggPSAnR2VuZGVyIElkZW50aXR5JywgeSA9ICdQZXJjZW50YWdlJykgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIkFnZSIpCgojIExldCdzIHRha2UgYSBsb29rIGF0IG91ciBzdGF0aWMgZ3JhcGgKcApgYGAKCkhtbSBva2F5LiBOb3QgdG9vIHNoYWJieSwgYnV0IHdlJ3JlIGRlZmluaXRlbHkgZ29pbmcgdG8gaGF2ZSB0byBkbyBzb21ldGhpbmcgYWJvdXQgb3VyIHgtYXhpcyBsYWJlbHMsIGFzIHJpZ2h0IG5vdyBldmVyeXRoaW5nIGlzIHByZXR0eSBjbHV0dGVyZWQuIE1heWJlIHdlIGNvdWxkIHJvdGF0ZSB0aGVtLCBvciBqdXN0IHJlbmFtZSB0aGVtLiBXZSdsbCBnZXQgcm91bmQgdG8gaXQuIEJ1dCBmb3Igbm93LCBsZXQncyBtYWtlIHRoaXMgdGhpbmcgaW50ZXJhY3RpdmUuCgpgYGB7cn0KIyBDb252ZXJ0IGdncGxvdCBvYmplY3QgdG8gYSBwbG90bHkgb2JqZWN0IGZvciBpbnRlcmFjdGl2aXR5CmZpZyA8LSBnZ3Bsb3RseShwLCB0b29sdGlwID0gYygieSIsICJmaWxsIiwgInRleHQiKSwgd2lkdGggPSA3MDAsIGhlaWdodCA9IDUwMCkgICMgU3BlY2lmeSB0b29sdGlwIGNvbXBvbmVudHMKCgojIExldCdzIGNoZWNrIGl0IG91dApmaWcKCmBgYAoKYGBge3J9CnRpY2t2YWxzIDwtIDE6bGVuZ3RoKGxldmVscyhkZiRnZW5kZXJfaWRlbnRpdHkpKQoKIyBDdXN0b20gdGljayBsYWJlbHMgY29ycmVzcG9uZGluZyB0byB0aGUgbGV2ZWxzCnRpY2t0ZXh0IDwtIGMoCiAgIkNpc2dlbmRlciIsIAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgaWRlbnRpdGllcyIsCiAgIk5vdCBhbnN3ZXJlZCIKKQoKYGBgCgojIyBUb29sdGlwcyAKCldoZW4gdXNpbmcgZGlmZmVyZW50IFIgbGlicmFyaWVzIGdlYXJlZCB0b3dhcmRzIGludGVyYWN0aXZlIHZpc3VhbGlzYXRpb25zLCB5b3UnbGwgb2Z0ZW4gY29tZSBhY3Jvc3MgJ3Rvb2x0aXBzJy4gVGhlc2UgYXJlIHNtYWxsIGJveGVzIHRoYXQgcHJvdmlkZSBpbmZvcm1hdGlvbiB3aGVuIGEgdXNlciBob3ZlcnMgb3ZlciBhIHBhcnQgb2YgYSBkYXRhIHZpc3VhbGlzYXRpb24gc3VjaCBhczogYSBwb2ludCBvbiBhIGdyYXBoLCBhIGJhciBpbiBhIGJhciBjaGFydCwgb3IgYSBzZWdtZW50IGluIGEgcGllIGNoYXJ0LiBUaGV5IGFyZSB1c2VkIHRvIGRpc3BsYXkgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBwb2ludCBvciBvYmplY3QsIHByb3ZpZGluZyBtb3JlIGNvbnRleHQgd2l0aG91dCBjbHV0dGVyaW5nIHVwIHRoZSBjaGFydC4gCgoKYGBge3J9CiMgU3BlY2lmeSBjdXN0b20gdGljayBsYWJlbHMgd2l0aCB0aGUgY29ycmVzcG9uZGluZyB0aWNrIHZhbHVlcwpmaWcgPC0gZmlnICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gJ0Rpc3RyaWJ1dGlvbiBvZiBHZW5kZXIgSWRlbnRpdHkgQ2F0ZWdvcmllcyBBbW9uZyBBZ2UgR3JvdXBzJywgeCA9IDAuNSksCiAgICB4YXhpcyA9IGxpc3QoCiAgICAgIHRpdGxlID0gbGlzdCgnR2VuZGVyIElkZW50aXR5JywKICAgICAgc3RhbmRvZmYgPSAxNSksCiAgICAgIHRpY2ttb2RlID0gImFycmF5IiwKICAgICAgdGlja3ZhbHMgPSB0aWNrdmFscywKICAgICAgdGlja3RleHQgPSB0aWNrdGV4dCwKICAgICAgdGlja2FuZ2xlID0gLTQ1CiAgICApLAogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gbGlzdCgnUGVyY2VudGFnZScsCiAgICAgICAgICAgICAgICAgc3RhbmRvZmYgPSAyNSkpKQoKZmlnCmBgYAoKIyMgRGF0YXNldCAyCgpgYGB7cn0KIyBMb2FkIGluIGRhdGFzZXQKZGYyIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2V0aG5pYy5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBhdCB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlCmhlYWQoZGYyLCAxMCkKYGBgCgpgYGB7cn0KIyBSZW1vdmUgYWxsIHRleHQgd2l0aGluIHBhcmVudGhlc2VzIGZyb20gY29sdW1uIG5hbWVzIGFuZCByZXBsYWNlIGl0IHdpdGggYW4gZW1wdHkgc3RyaW5nCgojIHRpbGRlIG9wZXJhdG9yICh+KSB1c2VkIHRvIGFwcGx5IGZ1bmN0aW9uICdnc3ViJyB0byBlYWNoIGNvbG5hbWUKIyAueCByZXByZXNlbnRzIGVhY2ggY29sbmFtZSB0aGF0IGdzdWIgd2lsbCBiZSBhcHBsaWVkIHRvCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZV93aXRoKH4gZ3N1YigiXFxzKlxcKFteKV0qXFwpIiwgIiIsIC54KSkKYGBgCgpgYGB7cn0KIyBMb3dlcmNhc2UgYWxsIHRleHQgaW4gY29sdW1uIG5hbWVzIGFuZCByZXBsYWNlIHNwYWNlcyB3aXRoIHVuZGVyc2NvcmVzCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZV93aXRoKH4gdG9sb3dlcihnc3ViKCIgIiwgIl8iLCAueCkpKQpgYGAKCmBgYHtyfQojIFNob3J0ZW4gdGhlIGxvY2FsIGF1dGhvcml0eSBjb2x1bW4gbmFtZXMgYXMgdGhleSBhcmUgd2F5IHRvbyBsb25nCmRmMiA8LSBkZjIgJT4lIAogIHJlbmFtZShMQV9jb2RlID0gbG93ZXJfdGllcl9sb2NhbF9hdXRob3JpdGllc19jb2RlLAogICAgICAgICBMQV9uYW1lID0gbG93ZXJfdGllcl9sb2NhbF9hdXRob3JpdGllcykKCmBgYAoKYGBge3J9CiMgTGV0J3Mgc2VlIGlmIGl0IHdvcmtlZApjb2xuYW1lcyhkZjIpCmBgYApgYGB7cn0KIyBSZW1vdmUgJ0RvZXMgbm90IGFwcGx5JyBjYXRlZ29yaWVzIGZvciB0aGUgZ2VuZGVyIGlkZW50aXR5IGFuZCBldGhuaWMgZ3JvdXAgY29sdW1ucwpkZjIgPC0gZGYyICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgsIGV0aG5pY19ncm91cF9jb2RlICE9IC04KQpgYGAKCmBgYHtyfQojIERpc3BsYXkgdGhlIGZpcnN0IDMwIHJvd3MKaGVhZChkZjIsIDMwKQpgYGAKCiMjIERhdGEgcHJlLXByb2Nlc3NpbmcKCiMjIyBDYWxjdWxhdGUgJSBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIExBCgpgYGB7cn0KIyBGaXJzdCwgd2UncmUgZ29pbmcgdG8gZ3JvdXAgb3VyIGRhdGEgYnkgTEFfbmFtZSwgRXRobmljIGdyb3VwLCBhbmQgc3VtIG91ciBvYnNlcnZhdGlvbnMKIyBUaGlzIGxlYXZlcyB1cyB3aXRoIHRoZSB0b3RhbCBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIGxvY2FsIGF1dGhvcml0eQpldGhuaWNfdG90YWxzIDwtIGRmMiAlPiUKICBncm91cF9ieShMQV9uYW1lLCBldGhuaWNfZ3JvdXApICU+JQogIHN1bW1hcmlzZShFdGhuaWNfc3VtID0gc3VtKG9ic2VydmF0aW9uLCBuYS5ybSA9IFRSVUUpKSAlPiUKICB1bmdyb3VwKCkKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIHRvIGNoZWNrCmhlYWQoZXRobmljX3RvdGFscykKYGBgCmBgYHtyfQojIENhbGN1bGF0ZSB0b3RhbCBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggbG9jYWwgYXV0aG9yaXR5CmxhX3RvdGFscyA8LSBkZjIgJT4lCiAgZ3JvdXBfYnkoTEFfbmFtZSkgJT4lCiAgc3VtbWFyaXNlKExBX3N1bSA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKGxhX3RvdGFscykKYGBgCgpgYGB7cn0KIyBNZXJnZSB0aGUgc3VtbWVkIGV0aG5pYyBncm91cCBkYXRhIHdpdGggdGhlIHRvdGFsIExBIG9ic2VydmF0aW9ucyB0byBjYWxjdWxhdGUgcGVyY2VudGFnZSBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIExBCm1lcmdlZCA8LSBtZXJnZShldGhuaWNfdG90YWxzLCBsYV90b3RhbHMsIGJ5ID0gIkxBX25hbWUiKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBlYWNoIGV0aG5pYyBncm91cCB3aXRoaW4gZWFjaCBsb2NhbCBhdXRob3JpdHkKbWVyZ2VkIDwtIG1lcmdlZCAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IHJvdW5kKChFdGhuaWNfc3VtIC8gTEFfc3VtICogMTAwKSwgMikpCmBgYAoKCmBgYHtyfQojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKG1lcmdlZCwgMTApCmBgYAoKIyMjIENhbGN1bGF0ZSBOb24tUmVzcG9uc2UgUmF0ZXMgV2l0aGluIExBcwoKYGBge3J9CiMgTm93IGNhbGN1bGF0ZSB0aGUgbm9uLXJlc3BvbnNlICUgZm9yIGVhY2ggZXRobmljIGdyb3VwIGluIGVhY2ggTEEKIyBUaGlzIGludm9sdmVzIGdyb3VwaW5nIGJ5IExBX25hbWUsIGV0aG5pY19ncm91cCwgYW5kIHN1bW1pbmcgb2JzZXJ2YXRpb25zIGFnYWluCmV0aG5pY19ncm91cF90b3RhbHMgPC0gZGYyICU+JQogIGdyb3VwX2J5KExBX25hbWUsIGV0aG5pY19ncm91cCkgJT4lCiAgc3VtbWFyaXNlKEV0aG5pY19ncm91cF90b3RhbCA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKYGBge3J9CiMgVGhpcyBpcyB0aGUgc3VtIG9mIG5vbi1yZXNwb25zZXMgZm9yIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIExBCm5vbl9yZXNwb25zZV90b3RhbHMgPC0gZGYyICU+JQogIGZpbHRlcihnZW5kZXJfaWRlbnRpdHkgPT0gJ05vdCBhbnN3ZXJlZCcpICU+JQogIGdyb3VwX2J5KExBX25hbWUsIGV0aG5pY19ncm91cCkgJT4lCiAgc3VtbWFyaXNlKE5vbl9yZXNwb25zZV90b3RhbCA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKYGBge3J9CmhlYWQobm9uX3Jlc3BvbnNlX3RvdGFscykKYGBgCmBgYHtyfQojIE1lcmdlIHRoZSB0b3RhbHMgd2l0aCB0aGUgbm9uLXJlc3BvbnNlIHRvdGFscwptZXJnZWRfZGF0YSA8LSBtZXJnZShldGhuaWNfZ3JvdXBfdG90YWxzLCBub25fcmVzcG9uc2VfdG90YWxzLCBieSA9IGMoIkxBX25hbWUiLCAiZXRobmljX2dyb3VwIiksIGFsbC54ID0gVFJVRSkKCgpoZWFkKG1lcmdlZF9kYXRhKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgbm9uLXJlc3BvbnNlIHBlcmNlbnRhZ2UgZm9yIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIExBCm1lcmdlZF9kYXRhIDwtIG1lcmdlZF9kYXRhICU+JQogIG11dGF0ZShFdGhfTlJfUGVyYyA9IHJvdW5kKChOb25fcmVzcG9uc2VfdG90YWwgLyBFdGhuaWNfZ3JvdXBfdG90YWwgKiAxMDApLCAyKSkKYGBgCgoKYGBge3J9CmhlYWQobWVyZ2VkX2RhdGEpCmBgYAoKYGBge3J9CiMgTWVyZ2UgdGhlIG5vbi1yZXNwb25zZSBkYXRhIHdpdGggdGhlIHBlcmNlbnRhZ2Ugb2YgZWFjaCBldGhuaWMgZ3JvdXAgd2l0aGluIGVhY2ggTEEKbnIgPC0gbWVyZ2UobWVyZ2VkX2RhdGEsIHNlbGVjdChtZXJnZWQsIExBX25hbWUsIGV0aG5pY19ncm91cCwgUGVyY2VudGFnZSksIGJ5ID0gYygiTEFfbmFtZSIsICJldGhuaWNfZ3JvdXAiKSkKCmhlYWQobnIpCmBgYAoKIyMgSW50ZXJhY3RpdmUgc2NhdHRlcnBsb3QKCkluIHRoaXMgc2VjdGlvbiB3ZSdyZSBnb2luZyB0bzoKCjEuIENyZWF0ZSBhIHNpbXBsZSBzY2F0dGVycGxvdCBleHBsb3JpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwZXJjZW50YWdlIG9mIGFzaWFuIGNpdGl6ZW5zIHdpdGhpbiBsb2NhbCBhdXRob3JpdGllcyBhbmQgdGhlaXIgbm9uLXJlc3BvbnNlIHJhdGVzCgoyLiBJbXBsZW1lbnQgYSB3aWRnZXQgdG8gdXBkYXRlIG91ciBzY2F0dGVycGxvdAoKYGBge3J9CiMgU3Vic2V0IGRhdGFmcmFtZSBzbyB3ZSBvbmx5IGhhdmUgcmVzcG9uc2VzIGZyb20gdGhlIGFzaWFuIGV0aG5pYyBncm91cAoKYXNpYW4gPC0gbnIgJT4lCiAgZmlsdGVyKGV0aG5pY19ncm91cCA9PSAnQXNpYW4sIEFzaWFuIEJyaXRpc2ggb3IgQXNpYW4gV2Vsc2gnKQoKaGVhZChhc2lhbikKYGBgCgpgYGB7cn0KIyBJbml0aWFsaXplIGZpZ3VyZQpmaWcgPC0gcGxvdF9seShkYXRhID0gYXNpYW4sCiAgICAgICAgICAgICAgIHggPSB+UGVyY2VudGFnZSwKICAgICAgICAgICAgICAgeSA9IH5FdGhfTlJfUGVyYywKICAgICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZSgnTEEgTmFtZTonLCBMQV9uYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPlBlcmNlbnRhZ2U6Jywgc3ByaW50ZigiJS4yZiIsIFBlcmNlbnRhZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFJhdGU6Jywgc3ByaW50ZigiJS4yZiUlIiwgRXRoX05SX1BlcmMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFRvdGFsOicsIE5vbl9yZXNwb25zZV90b3RhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPkV0aG5pYyBHcm91cCBUb3RhbDonLCBFdGhuaWNfZ3JvdXBfdG90YWwpLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIsCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsICAjIFNwZWNpZnkgbWFya2VyIHBvaW50cwogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCAgIyBHcmFwaCB0eXBlIC0gc2NhdHRlcnBsb3QKICAgICAgICAgICAgICAgbmFtZSA9ICdBc2lhbicpICAjIERlZmF1bHQgdmlzaWJsZSBncmFwaAoKCiMgQ3VzdG9taXplIGxheW91dCAKZmlnIDwtIGZpZyAlPiUKICBsYXlvdXQodGl0bGUgPSAnTm9uLVJlc3BvbnNlIFJhdGVzIG9mIHRoZSBBc2lhbiBFdGhuaWMgR3JvdXAgQWNyb3NzIExvY2FsIEF1dGhvcml0aWVzJywKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1BlcmNlbnRhZ2Ugb2YgRXRobmljIEdyb3VwJyksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdOb24tcmVzcG9uc2UgUmF0ZScpLAogICAgICAgICB3aWR0aCA9IDkwMCwKICAgICAgICAgaGVpZ2h0ID0gOTAwKQoKIyBTaG93IHRoZSBwbG90CmZpZwpgYGAKCiMjIERyb3Bkb3duIHNlbGVjdGlvbgoKV2hhdCB3ZSdyZSBnb2luZyB0byBkbyBub3csIGlzIHVzZSBQbG90bHkncyAndXBkYXRlbWVudXMnIGluIGNvbmp1bmN0aW9uIHdpdGggdGhlICd1cGRhdGUnIG1ldGhvZCB0byBjcmVhdGUgYSBkcm9wZG93biB3aGVyZSB3ZSBjYW4gc3dpdGNoIGJldHdlZW4gdGhlIEFzaWFuIGV0aG5pYyBncm91cCwgYW5kIFdoaXRlLiAKCiMjIyBTdGVwIDE6IEluaXRpYWxpc2UgZmlndXJlIGFuZCBhZGQgdHJhY2VzCgpXZSdsbCBzdGFydCBieSBjcmVhdGluZyBhIHBsb3RfbHkgZmlndXJlLiBXZSB1c2UgcGxvdGx5IGZpZ3VyZXMgaGVyZSBpbnN0ZWFkIG9mIGdncGxvdGx5LCBiZWNhdXNlIHBsb3RfbHkgb2JqZWN0cyBvZmZlciBtb3JlIGNvbnRyb2wgb3ZlciBob3cgcGxvdHMgYXJlIGNvbnN0cnVjdGVkLiBJdCBhbGxvd3MgdXMgdG8gYWRkICd0cmFjZXMnLCB3aGljaCByZWZlciB0byBhIHNldCBvZiBkYXRhLiBJbiBvdXIgZXhhbXBsZSwgd2Ugd2FudCB0byBhZGQgYSB0cmFjZSB3aXRoIHRoZSBkYXRhIHBvaW50cyByZWxhdGluZyB0byBvdXIgYXNpYW4gZXRobmljIGdyb3VwLCBhbmQgYW5vdGhlciBvbmUgZm9yIG91ciB3aGl0ZSBldGhuaWMgZ3JvdXAuIFRoaXMgd2lsbCBzdGFydCB0byBtYWtlIHNlbnNlIHdoZW4gd2UgbG9vayBhdCB0aGUgY29kZSBiZWxvdy4gCgpgYGB7cn0KIyBJbml0aWFsaXplIGEgUGxvdGx5IGZpZ3VyZQpmaWcgPC0gcGxvdF9seSgpCgpmaWcKCmBgYAoKYGBge3J9CiMgQWRkIHRyYWNlIGZvciB0aGUgQXNpYW4gZXRobmljIGdyb3VwCmZpZyA8LSBmaWcgJT4lIGFkZF90cmFjZSgKICBkYXRhID0gbnJbbnIkZXRobmljX2dyb3VwID09ICdBc2lhbiwgQXNpYW4gQnJpdGlzaCBvciBBc2lhbiBXZWxzaCcsXSwKICB4ID0gflBlcmNlbnRhZ2UsCiAgeSA9IH5FdGhfTlJfUGVyYywKICB0ZXh0ID0gfnBhc3RlKCdMQSBOYW1lOicsIExBX25hbWUpLAogIHR5cGUgPSAnc2NhdHRlcicsCiAgbW9kZSA9ICdtYXJrZXJzJywKICBuYW1lID0gJ0FzaWFuJywKICBob3ZlcmluZm8gPSAndGV4dCt4K3knLAogIHZpc2libGUgPSBUCikKCiMgQWRkIHRyYWNlIGZvciB0aGUgV2hpdGUgZXRobmljIGdyb3VwCmZpZyA8LSBmaWcgJT4lIGFkZF90cmFjZSgKICBkYXRhID0gbnJbbnIkZXRobmljX2dyb3VwID09ICdXaGl0ZTogRW5nbGlzaCwgV2Vsc2gsIFNjb3R0aXNoLCBOb3J0aGVybiBJcmlzaCBvciBCcml0aXNoJyxdLAogIHggPSB+UGVyY2VudGFnZSwKICB5ID0gfkV0aF9OUl9QZXJjLAogIHRleHQgPSB+cGFzdGUoJ0xBIE5hbWU6JywgTEFfbmFtZSksCiAgdHlwZSA9ICdzY2F0dGVyJywKICBtb2RlID0gJ21hcmtlcnMnLAogIG5hbWUgPSAnV2hpdGUnLAogIGhvdmVyaW5mbyA9ICd0ZXh0K3greScsCiAgdmlzaWJsZSA9IEYKKQoKZmlnCmBgYAoKCmBgYHtyfQoKIyBEZWZpbmUgZHJvcGRvd24gYnV0dG9ucyBmb3IgaW50ZXJhY3Rpdml0eQpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgdGl0bGUgPSAiTm9uLVJlc3BvbnNlIFJhdGVzIEFjcm9zcyBMb2NhbCBBdXRob3JpdGllcyIsCiAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgRXRobmljIEdyb3VwIiksCiAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIk5vbi1yZXNwb25zZSBSYXRlIiksCiAgc2hvd2xlZ2VuZCA9IEZBTFNFLAogIHVwZGF0ZW1lbnVzID0gbGlzdCgKICAgIGxpc3QoCiAgICAgIHR5cGUgPSAiZHJvcGRvd24iLAogICAgICBidXR0b25zID0gbGlzdCgKICAgICAgICBsaXN0KAogICAgICAgICAgbWV0aG9kID0gInVwZGF0ZSIsCiAgICAgICAgICBhcmdzID0gbGlzdChsaXN0KCJ2aXNpYmxlIiA9IGxpc3QoVFJVRSwgRkFMU0UpKSwKICAgICAgICAgICAgICAgICAgICAgIGxpc3QoInRpdGxlIiA9ICJOb24tUmVzcG9uc2UgUmF0ZXMgb2YgdGhlIEFzaWFuIEV0aG5pYyBHcm91cCBBY3Jvc3MgTG9jYWwgQXV0aG9yaXRpZXMiKSksCiAgICAgICAgICBsYWJlbCA9ICJBc2lhbiIKICAgICAgICApLAogICAgICAgIGxpc3QoCiAgICAgICAgICBtZXRob2QgPSAidXBkYXRlIiwKICAgICAgICAgIGFyZ3MgPSBsaXN0KGxpc3QoInZpc2libGUiID0gbGlzdChGQUxTRSwgVFJVRSkpLAogICAgICAgICAgICAgICAgICAgICAgbGlzdCgidGl0bGUiID0gIk5vbi1SZXNwb25zZSBSYXRlcyBvZiB0aGUgV2hpdGUgRXRobmljIEdyb3VwIEFjcm9zcyBMb2NhbCBBdXRob3JpdGllcyIpKSwKICAgICAgICAgIGxhYmVsID0gIldoaXRlIgogICAgICAgICkKICAgICAgKQogICAgKQogICkKKQoKIyBEaXNwbGF5IHRoZSBmaWd1cmUKZmlnCmBgYAoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCg==